import geometry.glCube;
import geometry.glPlane;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Random;
import math_generic.Vector3;
import objReader.ModelReader;
import objReader.Vertex3;
import objReader.objModel;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.Display;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.*;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

public class GUI_test 
{
	long lastFPS;
	int fps;
	long lastFrame; /* time at last frame */

	final float PI_OVER_180 	= (float) Math.PI/180;
	final float DISTANCE_NEAR 	= 100.0f;
	final float DISTANCE_MEDIUM = 500.0f;
	final float DISTANCE_FAR 	= 1000.0f;

	/* Camera */
	Camera observer;
	
	/* object models */
	ArrayList<objModel> models;
	
	/* Textures */
	ArrayList<Texture> textures;
	
	ArrayList<glCube> cubeArr;
	glPlane plane;
	
	Random rand;
	boolean renderEdges;
	boolean lightsEnabled;
	
	/* Light testing */
	private float lightAmbient[] = { 0.5f, 0.5f, 0.5f, 1.0f };  // Ambient Light Values ( NEW )
    private float lightDiffuse[] = { 1.0f, 1.0f, 1.0f, 1.0f };      // Diffuse Light Values ( NEW )
    private float lightSpecular[] = { 1.0f, 1.0f, 1.0f, 1.0f };      // Diffuse Light Values ( NEW )
    private float lightPosition[] = { 0.0f, 10.0f, -1.0f, 1.0f }; // Light Position ( NEW )
    private float spotDirection[] = { 0.0f, -1.0f, 0.0f, 1.0f }; // Light Position ( NEW )
	
	public void start()
	{
		/* Player camera */
 		observer = new Camera(0, 23.0f, 0);
		
		cubeArr = new ArrayList<glCube>();
		textures = new ArrayList<Texture>();
		models = new ArrayList<objModel>();
		
		rand = new Random();
		
		/* Create the display*/
		try
		{
			Display.setDisplayMode(new DisplayMode(800, 600));
			Display.create();
		} 
		catch(LWJGLException ex)
		{
			ex.printStackTrace();
			System.exit(0);
		}
		
		/* Init OPENGL here */
		initOpenGL();

		models = ModelReader.readModelFile("src/data/models/terrain.obj");  
		//models.get(0).scaleObj(3f, 3f, 3f);
		models.get(1).scaleObj(20f, 20f, 20f);
		
		//for(int i = 0; i < 100; i++)
		//{
		//	cubeArr.add(new glCube(50*rand.nextFloat() - 25, 50*rand.nextFloat() - 25, -50*rand.nextFloat(), 5.0f, true));
		//}
		
		/* scale the 2nd model */
		//models.get(1).scaleObj(0.60f, 0.60f, 0.60f);

		/* Create objects */
		//plane = new glPlane(-500.0f, 0, 500.0f, 500.0f, 0, -500.0f );	

		/* Center mouse in screen, and grab it */
		Mouse.setCursorPosition(Display.getDisplayMode().getWidth()/2, Display.getDisplayMode().getHeight()/2);
		Mouse.setGrabbed(true);
		
		getDelta();
		lastFPS = getTime();
		
		/* Main loop */
		while(!Display.isCloseRequested())
		{
			/* Update frames per second */
			int delta = getDelta();
			updateFPS();
			
			/* Read user input */
			readMouse();
			readKeys();

			/* Render display */
			renderGL();
			
			Display.update();
			Display.sync(60);
		}
		
		Display.destroy();
	}
	
	public static void main(String[] args) 
	{
		// TODO Auto-generated method stub
		GUI_test inst = new GUI_test();
		inst.start();
	}
	
	/* Function explanation: 
	 * 
	 * gluLookAt(...) contains 3 sets of parameters (9 total). 
	 * The first set represents the camera position.
	 * The second set represents the point we are looking at.
	 * The third set indicates a vector that points up. */
	public void renderGL()
	{
		/* Clear The Screen And The Depth Buffer */
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		GL11.glLoadIdentity(); /* Reset matrix */

		/* Update Player World location */ 
		
		//System.out.println("y(" + observer.getCameraPosition().y + " ). vel(" + observer.getDirectionVelocity() + ")");

		/*Free moving camera (based on its velocity vector) */
		GLU.gluLookAt(observer.getCameraPosition().x,
					  observer.getCameraPosition().y,
					  observer.getCameraPosition().z,
					  observer.getCameraPosition().x + observer.lookingWhere().x, 
					  observer.getCameraPosition().y + observer.lookingWhere().y, 
					  observer.getCameraPosition().z + observer.lookingWhere().z,
					  0, 1.0f, 0);

		/* Render all models */
		for(objModel m : models)
		{
			m.renderModel(false);
		}
	}
	
	public void initOpenGL()
	{
		//GL11.glEnable(GL11.GL_COLOR_MATERIAL); 
		GL11.glShadeModel(GL11.GL_SMOOTH); /* Smooth shadows */

		/* Depth buffer setup */
		GL11.glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // Black Background
        GL11.glClearDepth(1.0f); // Depth Buffer Setup
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glDepthFunc(GL11.GL_LEQUAL);

		//GL11.glEnable(GL11.GL_CULL_FACE);
	    //GL11.glCullFace(GL11.GL_BACK);
	    
		/* Enable alpha blending */
		GL11.glEnable(GL11.GL_BLEND);
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

		GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
        GL11.glLoadIdentity(); // Reset The Projection Matrix
		GLU.gluPerspective(45.0f, 
						  (float)Display.getDisplayMode().getWidth()/(float)Display.getDisplayMode().getHeight(), 
						  0.1f, 
						  DISTANCE_FAR);
		
		GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
		GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
		
        ByteBuffer temp = ByteBuffer.allocateDirect(16);
        temp.order(ByteOrder.nativeOrder());
        GL11.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, (FloatBuffer)temp.asFloatBuffer().put(lightAmbient).flip());              // Setup The Ambient Light
        GL11.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, (FloatBuffer)temp.asFloatBuffer().put(lightDiffuse).flip());              // Setup The Diffuse Light
        GL11.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, (FloatBuffer)temp.asFloatBuffer().put(lightSpecular).flip());              // Setup The Diffuse Light
        GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, (FloatBuffer)temp.asFloatBuffer().put(lightPosition).flip()); 
        //GL11.glLight(GL11.GL_LIGHT0, GL11.GL_SPOT_DIRECTION, (FloatBuffer)temp.asFloatBuffer().put(spotDirection).flip());
        GL11.glLightf(GL11.GL_LIGHT0, GL11.GL_SPOT_CUTOFF, 15.0f); /* Make spot light with distance cutoff (30 degrees) */
        
        //GL11.glEnable(GL11.GL_LIGHT0);                          // Enable Light One
        
        /* Sun light */
        GL11.glLight(GL11.GL_LIGHT1, GL11.GL_AMBIENT, (FloatBuffer)temp.asFloatBuffer().put(lightAmbient).flip());              // Setup The Ambient Light
        GL11.glLight(GL11.GL_LIGHT1, GL11.GL_DIFFUSE, (FloatBuffer)temp.asFloatBuffer().put(lightDiffuse).flip());              // Setup The Diffuse Light
        GL11.glLight(GL11.GL_LIGHT1, GL11.GL_SPECULAR, (FloatBuffer)temp.asFloatBuffer().put(lightSpecular).flip());              // Setup The Diffuse Light
        GL11.glLight(GL11.GL_LIGHT1, GL11.GL_POSITION,(FloatBuffer)temp.asFloatBuffer().put(lightPosition).flip());         // Position The Light
        GL11.glEnable(GL11.GL_LIGHT1);                          // Enable Light One
        
        /* Enable lighting */
        GL11.glEnable(GL11.GL_LIGHTING);
	}
	
	/* Return system time in milliseconds */
	public long getTime()
	{
		return (Sys.getTime()*1000/Sys.getTimerResolution());
	}
	
	/* milliseconds passed since last call */
	public int getDelta()
	{
		long time = getTime();
		int delta = (int)(time - lastFrame);
		lastFrame = time;
		
		return delta;
	}
	
	public void updateFPS()
	{
		if((getTime() - lastFPS) > 1000)
		{
			Display.setTitle("FPS: " + fps);
			fps = 0;
			lastFPS += 1000;
		}
		
		fps++;
	}
	
	public void readMouse()
	{
		if(Mouse.isButtonDown(0) && Mouse.isInsideWindow() && !Mouse.isGrabbed())
			Mouse.setGrabbed(true); 
		
		/* Mouse Look */
		if(Mouse.isGrabbed())
		{
			observer.incrCameraAngle(0.1f*Mouse.getDX(), 
									 0.1f*Mouse.getDY()); 
		}
	}
	 
	/* Navigation */
	public void readKeys()
	{ 
		/* Reset velocities */
		observer.setCameraVelocity(0, 0, 0);
		
		/* Front/Back walking */
		if(Keyboard.isKeyDown(Keyboard.KEY_W))
			observer.moveForward(observer.MAX_VELOCITY_WALK); /* Forward */
		else if(Keyboard.isKeyDown(Keyboard.KEY_S))
			observer.moveForward(-observer.MAX_VELOCITY_WALK); /* Backwards */
			
		/* Left/Right */
		if(Keyboard.isKeyDown(Keyboard.KEY_A))
			observer.moveRight(-observer.MAX_VELOCITY_WALK);
		else if(Keyboard.isKeyDown(Keyboard.KEY_D))
			observer.moveRight(observer.MAX_VELOCITY_WALK);
		
		/* Up/down Movement */
		if(Keyboard.isKeyDown(Keyboard.KEY_SUBTRACT))
			observer.moveUp(observer.MAX_VELOCITY_WALK);
		else if(Keyboard.isKeyDown(Keyboard.KEY_ADD))
			observer.moveUp(-observer.MAX_VELOCITY_WALK);
		
		if(Keyboard.isKeyDown(Keyboard.KEY_SPACE))
			observer.toggleRunning();
		
		/* If ESC key is pressed, drop the mouse */
		if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))
		{
			Mouse.setGrabbed(false);
			
			// maybe pause
		}
		
		/* Switch between Ortho and Perspective views */
		if(Keyboard.isKeyDown(Keyboard.KEY_O))
			toOrtho();
		if(Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) && Keyboard.isKeyDown(Keyboard.KEY_O))
			toPerspective();
		
		if(Keyboard.isKeyDown(Keyboard.KEY_E))
			renderEdges = !renderEdges;
		if(Keyboard.isKeyDown(Keyboard.KEY_L))
		{
			lightsEnabled = !lightsEnabled;
			
			if(lightsEnabled)
				GL11.glEnable(GL11.GL_LIGHT0);
			else
				GL11.glDisable(GL11.GL_LIGHT0);
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_NUMPAD8))
			lightPosition[2] += 0.1f;
		else if(Keyboard.isKeyDown(Keyboard.KEY_NUMPAD8))
			lightPosition[2] -= 0.1f;
	}
	
	public void toOrtho()
	{
		GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
        GL11.glLoadIdentity(); // Reset The Projection Matrix
        
		GL11.glOrtho(-Display.getDisplayMode().getWidth()/10, 
					  Display.getDisplayMode().getWidth()/10, 
					 -Display.getDisplayMode().getHeight()/10, 
					  Display.getDisplayMode().getHeight()/10, 
					 -100, 	/* z-near */
					  100);	/* z-far*/
		
		GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
	}
	
	public void toPerspective()
	{
		GL11.glMatrixMode(GL11.GL_PROJECTION); // Select The Projection Matrix
        GL11.glLoadIdentity(); // Reset The Projection Matrix
        
		GLU.gluPerspective(45.0f, 
						  (float)Display.getDisplayMode().getWidth()/(float)Display.getDisplayMode().getHeight(), 
						  0.1f, 
						  100.0f);
		
		GL11.glMatrixMode(GL11.GL_MODELVIEW); // Select The Modelview Matrix
		GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
	}
	
	public void displayText(String txt, int x, int y)
	{
		 
	}
}
